/***************************************************************************
 * Copyright Andy Brummer 2004-2005
 * 
 * This code is provided "as is", with absolutely no warranty expressed
 * or implied. Any use is at your own risk.
 *
 * This code may be used in compiled form in any way you desire. This
 * file may be redistributed unmodified by any means provided it is
 * not sold for profit without the authors written consent, and
 * providing that this notice and the authors name is included. If
 * the source code in  this file is used in any commercial application
 * then a simple email would be nice.
 * 
 **************************************************************************/

using System;
using System.Collections;
using System.Collections.Specialized;
using BackupAssurance.Library.Enums;
//using System.Data.SqlTypes;

namespace BackupAssurance.Library.ModelsDB
{


    /// <summary>
    /// This class represents a simple schedule.  It can represent a repeating event that occurs anywhere from every
    /// second to once a month.  It consists of an enumeration to mark the interval and an offset from that interval.
    /// For example new ScheduledTime(Hourly, new TimeSpan(0, 15, 0)) would represent an event that fired 15 minutes
    /// after the hour every hour.
    /// </summary>
    [Serializable]
    public class ScheduledTime
    {
        public EventTimeBase _Base;
        public TimeSpan _Offset;
        public pcJobAction _JobAction;
        public DateTime _NextRunDateTime;
        public DateTime _LastRunTime;
        private bool _ShouldFireAll7Days;

        public bool ShouldFireAll7Days
        {
            get { return _ShouldFireAll7Days; }
            set { _ShouldFireAll7Days = value; }
        }

        public override string ToString()
        {
            return "" + Environment.NewLine + "[" + _JobAction.name + "]  JA:" + _JobAction.Id + "  Base:" + _Base.ToString() + " Offset:" + _Offset.ToString() + " LastRunTime" + _LastRunTime.ToString() + " nextruntime:" + _NextRunDateTime.ToString();
        }

        public ScheduledTime(EventTimeBase Base, TimeSpan Offset, pcJobAction JobAction, DateTime LastRunTime, bool shouldfireall7days)
        {
            DateTime localminvalue = DateTime.MinValue.AddYears(2000);
            _ShouldFireAll7Days = shouldfireall7days;
            if (LastRunTime == null || DateTime.Compare(LastRunTime, DateTime.MinValue) == 0 || DateTime.Compare(LastRunTime, localminvalue) == 0)
            {
                _LastRunTime = DateTime.Now;
            }
            else
            {
                _LastRunTime = LastRunTime;
            }

            _JobAction = JobAction;
            _Base = Base;
            _Offset = Offset;
            _JobAction = JobAction;
        }

        /// <summary>
        /// intializes a simple scheduled time element from a pair of strings.  
        /// Here are the supported formats
        /// 
        /// BySecond - single integer representing the offset in ms
        /// ByMinute - A comma seperate list of integers representing the number of seconds and ms
        /// Hourly - A comma seperated list of integers representing the number of minutes, seconds and ms
        /// Daily - A time in hh:mm:ss AM/PM format
        /// Weekly - n, time where n represents an integer and time is a time in the Daily format
        /// Monthly - the same format as weekly.
        /// 
        /// </summary>
        /// <param name="StrBase">A string representing the base enumeration for the scheduled time</param>
        /// <param name="StrOffset">A string representing the offset for the time.</param>
        public ScheduledTime(string StrBase, string StrOffset, pcJobAction JobAction, DateTime LastRunTime, bool shouldfireall7days)
        {
            //TODO:Create an IScheduled time factory method.
            _Base = (EventTimeBase)Enum.Parse(typeof(EventTimeBase), StrBase, true);
            Init(StrOffset);
            _JobAction = JobAction;
            _ShouldFireAll7Days = shouldfireall7days;
            DateTime localminvalue = DateTime.MinValue.AddYears(2000);
            if (LastRunTime == null || DateTime.Compare(LastRunTime, DateTime.MinValue) == 0 || DateTime.Compare(LastRunTime, localminvalue) == 0)
            {
                _LastRunTime = DateTime.Now;
            }
            else
            {
                _LastRunTime = LastRunTime;
            }
        }


        public DateTime STNextRunTime(DateTime LastTimeJobRan)
        {
            DateTime NextRun = LastTimeJobRan;
            DateTime updated;



            switch (_Base)
            {
                case EventTimeBase.Off:
                    {
                    }
                    break;
                case EventTimeBase.BySecond:
                    {
                        updated = trimmedstart(NextRun);
                        NextRun = updated + _Offset;
                    }
                    break;
                case EventTimeBase.ByMinute:
                    {
                        updated = trimmedstart(NextRun);
                        NextRun = updated + _Offset;
                    }
                    break;
                case EventTimeBase.Hourly:
                    {
                        updated = trimmedstart(NextRun);
                        NextRun = updated + _Offset;
                    }
                    break;
                case EventTimeBase.Daily:
                    {
                        updated = trimmedstart(NextRun);
                        NextRun = updated + _Offset;
                        if (NextRun < DateTime.Now)
                        {
                            updated = trimmedstart(NextRun);
                            updated = updated.AddDays(1);
                            NextRun = updated + _Offset;
                        }
                    }
                    break;
                case EventTimeBase.Weekly:
                    {
                        if (_ShouldFireAll7Days == true)
                        {
                            updated = trimmedstart(NextRun);
                            updated = updated + _Offset;
                            NextRun = updated;
                            if (NextRun < DateTime.Now)
                            {
                                do
                                {

                                    updated = updated.AddDays(1);
                                    updated = trimmedstart(updated);
                                    updated = updated + _Offset;
                                    if (updated > DateTime.Now)
                                    {
                                        NextRun = updated;
                                        break;
                                    }
                                } while (true);
                            }


                        }
                        else
                        {
                            updated = trimmedstart(NextRun);
                            updated = updated + _Offset;
                            NextRun = updated;
                            if (NextRun < DateTime.Now)
                            {
                                do
                                {
                                    updated = trimmedstart(updated);
                                    updated = updated.AddDays(1);
                                    updated = updated + _Offset;

                                    if (updated > DateTime.Now && ShouldWeeklyEventRunThisDate(updated))
                                    {
                                        NextRun = updated;
                                        break;
                                    }
                                } while (true);
                            }

                        }
                    }
                    break;
                case EventTimeBase.Monthly:
                    {
                        updated = trimmedstart(NextRun);
                        updated = updated.AddMonths(1);
                        NextRun = updated + _Offset;
                    }
                    break;
                case EventTimeBase.Once:
                    {
                    }
                    break;
                case EventTimeBase.RunOnce:
                    {
                    }
                    break;
                default:
                    break;
            }

            _NextRunDateTime = NextRun;
            return NextRun;
        }
        private bool ShouldWeeklyEventRunThisDate(DateTime testDate)
        {
            bool shouldruntoday = false;

            if ((testDate.DayOfWeek == DayOfWeek.Sunday) && (_JobAction.daySunday == true))
            {
                shouldruntoday = true;
            }
            if ((testDate.DayOfWeek == DayOfWeek.Monday) && (_JobAction.dayMonday == true))
            {
                shouldruntoday = true;
            }
            if ((testDate.DayOfWeek == DayOfWeek.Tuesday) && (_JobAction.dayTuesday == true))
            {
                shouldruntoday = true;
            }
            if ((testDate.DayOfWeek == DayOfWeek.Wednesday) && (_JobAction.dayWednesday == true))
            {
                shouldruntoday = true;
            }
            if ((testDate.DayOfWeek == DayOfWeek.Thursday) && (_JobAction.dayThursday == true))
            {
                shouldruntoday = true;
            }
            if ((testDate.DayOfWeek == DayOfWeek.Friday) && (_JobAction.dayFriday == true))
            {
                shouldruntoday = true;
            }
            if ((testDate.DayOfWeek == DayOfWeek.Saturday) && (_JobAction.daySaturday == true))
            {
                shouldruntoday = true;
            }

            return shouldruntoday;
        }
        private DateTime trimmedstart(DateTime LastTimeJobRan)
        {
            DateTime updated;

            switch (_Base)
            {
                case EventTimeBase.BySecond:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day, LastTimeJobRan.Hour, LastTimeJobRan.Minute, LastTimeJobRan.Second);
                    break;
                case EventTimeBase.ByMinute:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day, LastTimeJobRan.Hour, LastTimeJobRan.Minute, 0);
                    break;
                case EventTimeBase.Hourly:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day, LastTimeJobRan.Hour, 0, 0);
                    break;
                case EventTimeBase.Daily:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day);
                    break;
                case EventTimeBase.Weekly:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day);
                    //updated = (new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day)).AddDays(-(int)LastTimeJobRan.DayOfWeek);
                    break;
                case EventTimeBase.Monthly:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, 1);
                    break;
                case EventTimeBase.Once:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day, LastTimeJobRan.Hour, LastTimeJobRan.Minute, LastTimeJobRan.Second);
                    break;
                case EventTimeBase.RunOnce:
                    updated = new DateTime(LastTimeJobRan.Year, LastTimeJobRan.Month, LastTimeJobRan.Day, LastTimeJobRan.Hour, LastTimeJobRan.Minute, LastTimeJobRan.Second);
                    break;
                default:
                    throw new Exception("Invalid base specified for timer.");
            }

            return updated;
        }

        private void Init(string StrOffset)
        {

            switch (_Base)
            {
                case EventTimeBase.BySecond:
                    string[] ArrSeconds = StrOffset.Split(',');
                    _Offset = new TimeSpan(0, 0, 0, int.Parse(ArrSeconds[0]), 0);
                    break;
                case EventTimeBase.ByMinute:
                    string[] ArrMinute = StrOffset.Split(',');
                    _Offset = new TimeSpan(0, 0, int.Parse(ArrMinute[0]), int.Parse(ArrMinute[1]));
                    break;
                case EventTimeBase.Hourly:
                    string[] ArrHour = StrOffset.Split(',');
                    _Offset = new TimeSpan(int.Parse(ArrHour[0]), int.Parse(ArrHour[1]), 0);
                    break;
                case EventTimeBase.Daily:
                    DateTime Daytime = DateTime.Parse(StrOffset);
                    _Offset = new TimeSpan(Daytime.Hour, Daytime.Minute, Daytime.Second);
                    break;
                case EventTimeBase.Weekly:
                    string[] ArrWeek = StrOffset.Split(',');
                    if (ArrWeek.Length != 2)
                        throw new Exception("Weekly offset must be in the format n, time where n is the day of the week starting with 0 for sunday");
                    DateTime WeekTime = DateTime.Parse(ArrWeek[1]);
                    _Offset = new TimeSpan(int.Parse(ArrWeek[0]), WeekTime.Hour, WeekTime.Minute, WeekTime.Second, WeekTime.Millisecond);
                    break;
                case EventTimeBase.Off:
                    break;
                case EventTimeBase.Once:
                    _Offset = TimeSpan.Zero;
                    break;
                case EventTimeBase.RunOnce:
                    _Offset = TimeSpan.Zero;
                    break;
                case EventTimeBase.Monthly:
                    string[] ArrMonth = StrOffset.Split(',');
                    if (ArrMonth.Length != 2)
                        throw new Exception("Monthly offset must be in the format n, time where n is the day of the month starting with 1 for the first day of the month.");
                    DateTime MonthTime = DateTime.Parse(ArrMonth[1]);
                    _Offset = new TimeSpan(int.Parse(ArrMonth[0]) - 1, MonthTime.Hour, MonthTime.Minute, MonthTime.Second, MonthTime.Millisecond);
                    break;
                default:
                    throw new Exception("Invalid base specified for timer.");
            }


        }


    }
}